#include "Map.h"

Map::Map()
	: mpSprites(nullptr)
	, mpTiles(nullptr)
	, mNumSprites(0)
	, mTileSize(0)
	, mColumns(0)
	, mRows(0)
{
}

Map::~Map()
{
}

void Map::Load(const char* pLevel, const char* pTexturePack)
{
	Unload();

	if (!LoadLevel(pLevel))
	{
		Unload();
		return;
	}

	if (!LoadTexturePack(pTexturePack))
	{
		Unload();
		return;
	}
}

void Map::Unload()
{
	for (int i = 0; i < mNumSprites; ++i)
	{
		mpSprites[i].Unload();
	}

	delete[] mpSprites;
	mpSprites = nullptr;

	delete[] mpTiles;
	mpTiles = nullptr;
	
	mNumSprites = 0;
	mTileSize = 0;
	mColumns = 0;
	mRows = 0;
}

void Map::Update(float deltaTime)
{
}

void Map::Render(const SVector2& viewOffset)
{
	for (int y = 0; y < mRows; ++y)
	{
		for (int x = 0; x < mColumns; ++x)
		{
			const int index = GetIndex(x, y);
			SVector2 pos((float)x * mTileSize, (float)y * mTileSize);
			SVector2 renderPosition = pos - viewOffset;
			mpSprites[mpTiles[index]].SetPosition(renderPosition);
			mpSprites[mpTiles[index]].Render();
		}
	}
}

bool Map::TestCollision(const SLineSegment& segment, SRect& rect) const
{
	if (segment.from.x < 0.0f || segment.from.x >= GetWidth() ||
		segment.from.y < 0.0f || segment.from.y >= GetHeight() ||
		segment.to.x < 0.0f || segment.to.x >= GetWidth() ||
		segment.to.y < 0.0f || segment.to.y >= GetHeight())
	{
		return false;
	}
	
	const int minX = (int)(segment.from.x / mTileSize);
	const int minY = (int)(segment.from.y / mTileSize);
	const int maxX = (int)(segment.to.x / mTileSize);
	const int maxY = (int)(segment.to.y / mTileSize);

	rect = SRect();

	bool collision = false;
	for (int y = minY; y <= maxY; ++y)
	{
		for (int x = minX; x <= maxX; ++x)
		{
			if (IsBlocked(x, y))
			{
				rect += GetBoundingBox(x, y);
				collision = true;
			}
		}
	}

	return collision;
}

bool Map::LoadLevel(const char* pLevel)
{
	FILE* pFile = nullptr;
	fopen_s(&pFile, pLevel, "r");
	if (pFile == nullptr)
	{
		return false;
	}
	
	fscanf_s(pFile, "%*s %d", &mTileSize);
	fscanf_s(pFile, "%*s %d", &mColumns);
	fscanf_s(pFile, "%*s %d", &mRows);

	mpTiles = new int[mColumns * mRows];

	fgetc(pFile);
	for (int y = 0; y < mRows; ++y)
	{
		for (int x = 0; x < mColumns; ++x)
		{
			const int index = GetIndex(x, y);
			mpTiles[index] = fgetc(pFile) - '0';
		}
		fgetc(pFile);
	}

	fclose(pFile);
	return true;
}

bool Map::LoadTexturePack(const char* pTexturePack)
{
	FILE* pFile = nullptr;
	fopen_s(&pFile, pTexturePack, "r");
	if (pFile == nullptr)
	{
		return false;
	}
	
	fscanf_s(pFile, "%*s %d", &mNumSprites);

	mpSprites = new SGE_Sprite[mNumSprites];

	char buffer[256];
	for (int i = 0; i < mNumSprites; ++i)
	{
		fscanf_s(pFile, "%s", buffer, 256);
		mpSprites[i].Load(buffer);
	}

	fclose(pFile);
	return true;
}

int Map::GetIndex(int x, int y) const
{
	return x + (y * mColumns);
}

bool Map::IsBlocked(int x, int y) const
{
	const int index = GetIndex(x, y);
	const int type = mpTiles[index];
	return (type > 1);	// HACK: assumes > 1 means not walkable
}

SRect Map::GetBoundingBox(int x, int y) const
{
	SRect boundingBox;
	boundingBox.min.x = (float)x * mTileSize;
	boundingBox.min.y = (float)y * mTileSize;
	boundingBox.max.x = (float)((x + 1) * mTileSize) - 1;
	boundingBox.max.y = (float)((y + 1) * mTileSize) - 1;
	return boundingBox;
}